using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.XPath;

namespace PaulRz.Util
{
	/// <summary>
	/// Summary description for Class1.
	/// </summary>
	public sealed class FUtil
	{	
		static FUtil()
		{
			Process proc = Process.GetCurrentProcess();
			ProcessModule procModule= proc.MainModule;
			directory = new FileInfo(procModule.FileName).DirectoryName+"\\";
			procModule.Dispose();
			// it's safe because it disposes only the wrapping object, not a process
			proc.Dispose();
		}
		
		public static bool IsNullEmpty(string s)
		{
			return ((s == null) || (s.Length == 0));
		}
		
		private static readonly string directory;
		public static string getConfigPath(string fileName) 
		{
            if (fileName == null)
                throw new ArgumentNullException("fileName");

            if(fileName.IndexOf(":")>0) //path contains a disk letter
				return fileName;
			else
			    return directory+fileName;
		}


		private static TextWriter logStreamWriter;
		private static DateTime timeLogCreation; // the date-time of last log's creation
		
		private static void createLogStream(string logDirectoryPath,  string name)
		{
			timeLogCreation = DateTime.Now;
			if(String.IsNullOrEmpty(logDirectoryPath))
			{
				logStreamWriter= StreamWriter.Synchronized(new StreamWriter(getConfigPath(timeLogCreation.ToString("ddMMyyyyHHmmss")+"-"+name+".log"),true));
			}
			else
			{
				DirectoryInfo info = new DirectoryInfo(getConfigPath(logDirectoryPath));
				if(!info.Exists)
					info.Create();
				
				logStreamWriter= StreamWriter.Synchronized(new StreamWriter(getConfigPath(logDirectoryPath + "\\" + timeLogCreation.ToString("ddMMyyyyHHmmss") + "-"+name+".log"), true));
			}
		}

		//[Conditional("DEBUG")]

		public static string arrangeLogRecord(string message)
		{
			return string.Format("{0} #{2}	{1}",DateTime.Now.ToString(FormatProvider.CRM),message,Thread.CurrentThread.GetHashCode());
		}

		public static void log(string msg, string logPathDirectory,string name)
		{
			try 
			{
				if(logStreamWriter==null) 
					createLogStream(logPathDirectory,name);
				if(timeLogCreation.Day!=(DateTime.Now).Day)
				{
					logStreamWriter.WriteLine("{0} #{1}	New Log file has been created due to day change", DateTime.Now.ToString(FormatProvider.CRM),Thread.CurrentThread.GetHashCode());
					logStreamWriter.Flush();
					logStreamWriter.Close();
					logStreamWriter = null;
					createLogStream(logPathDirectory,name);
				}
				string message=arrangeLogRecord(msg);
				logStreamWriter.WriteLine(message);
				Console.Out.WriteLine(message);
				logStreamWriter.Flush();
			} 
			catch(IOException e)
			{
				Console.WriteLine("Log record cannot be written due to IO errors: \n"+e);
			}
		}

		public static void log(string msg) 
		{
			log(msg, "","Dispatcher");
		}


		public static string getFileContent(string fileName) 
		{
			StreamReader sr=new StreamReader(getConfigPath(fileName));
			string result=sr.ReadToEnd();
			sr.Close();
			return result;
			
		}

		public static string getShortClassName(Type type) 
		{
            if (type == null)
                throw new ArgumentNullException("type");

            int index=type.Name.LastIndexOf(".");
			if(index<0)
				return type.Name;
			return type.Name.Substring(index+1);
		}

		/// <summary>
		/// Returs string representation of given object
		/// </summary>
		/// <param name="col"></param>
		/// <returns></returns>
		public static string ToString(object col)
		{
			if(col==null)
				return "<<null>>";


			if(col is string)
				return (string)col;

			if(col is XPathNodeIterator)
			{
				try
				{
					XPathNodeIterator it=((XPathNodeIterator)col);
					if(!it.MoveNext())
						return "";
					return it.Current.ToString();
				}
				catch (Exception)
				{
					return "<<WRONG ITERATOR>>";
				}
			}
			
			if(col is DictionaryEntry) {
				return ToString(((DictionaryEntry)col).Key)+"="+ToString(((DictionaryEntry)col).Value);
			}
			if(!(col is IEnumerable))
				return col.ToString();
			
			StringBuilder sb=new StringBuilder(); 
			
			sb.Append("[");
			bool first=true;
			foreach(object obj in (IEnumerable)col)
			{
				if(!first)
					sb.Append(", ");
				first=false;
				sb.Append(ToString(obj));
				
			}
			sb.Append("]");
			return sb.ToString();
		}

		public static string ToHexString(byte[] bytes)
		{
			char[] chars = new char[bytes.Length * 3];
			for (int i = 0; i < bytes.Length; i++)
			{
				int b = bytes[i];
				chars[i * 3] = hexDigits[b >> 4];
				chars[i * 3 + 1] = hexDigits[b & 0xF];
				chars[i * 3 + 2] = ' ';
			}
			return new string(chars);
		}//ToHexString


		static char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7','8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
		

#region Xml utils
			private static string[][] REPLACE=new string[][] {
																new string[] {"&lt;","<"},
																new string[] {"&gt;",">"},
																new string[] {"&quot;","\""},
																new string[] {"&amp;","&"}
															};
												  
			
		public static void testt()
		{
			/*string s="<r>"+xmlEntitiesEncode("qwer<sds>дравст\"'"+(char)20)+"</r>";
			Console.Out.WriteLine(s);
			XmlDocument d=parseXml(s);
			Console.Out.WriteLine(d.OuterXml);*/
			//0a 03 00 03 04 30 30 32

			Console.Out.WriteLine(Encoding.GetEncoding("ucs-2").GetString(
				new byte[] {0x05,0x0a,0x03,0x00,0x04,0x30,0x30,0x32}));

		}
		public static string XmlEntitiesEncode(string input)
			{
				
				StringBuilder sb=new StringBuilder(input.Length);
				foreach (char ch in input.ToCharArray())
				{
					string character=ch.ToString();
					foreach(string [] pair in REPLACE)
					{
						if(character==pair[1])
							character=pair[0];
					}
					if(ch<32 && ch!='\n' && ch!='\r')
						character="&#"+(byte)ch+";";
					sb.Append(character);
				}

				return sb.ToString();
				
				

			}

			public static string XmlEntitiesDecode(string input)
			{
				foreach(string [] pair in REPLACE)
				{
					input=input.Replace(pair[0],pair[1]);
				}
				return input;
			}

		
		
		public static Hashtable getAttributesMap(XmlDocument doc)
		{
			
			Hashtable result=new HashtableCI();
			
			addAttributesToHashtable(doc.DocumentElement.Attributes,result);

			XmlElement itemElement = (XmlElement)doc.GetElementsByTagName("Item")[0];
			if (itemElement == null)	
			{
				throw new Exception("Couldn't load attributes. An <Item> element is missing!");
			}

			foreach(XmlElement e in itemElement.GetElementsByTagName("Attribute")) 
			{
				result[e.GetAttribute("name")]=e.GetAttribute("value");   		
			}
			

			addAttributesToHashtable(itemElement.Attributes, result);
			
			
			XmlNodeList list = itemElement.GetElementsByTagName("Service");
			if(list.Count>0) 
			{
				result["resultcode"]=((XmlElement)list[0]).GetAttribute("resultcode");
				result["service_id"]=((XmlElement)list[0]).GetAttribute("id");
			}
			

			list=itemElement.GetElementsByTagName("Content");
			
			if(list.Count>0)
			{
				XmlElement contentElement=(XmlElement)list[0];
				//addAttributesToHashtable(contentElement.Attributes,result);
				result["content"]=contentElement.InnerText;
			}

			return result;
			
		}

		public static Hashtable getAttributesMap(IXPathNavigable doc)
		{
			XPathNavigator nav=doc.CreateNavigator();
		    return getAttributesMap(nav);
		}

	    public static Hashtable getAttributesMap(XPathNavigator nav)
	    {
	        Hashtable result = new HashtableCI();
	        addAttributesToHashtable(nav.Select("/Message/@*"),result);
	        addAttributesToHashtable(nav.Select("/Message//Item/@*"),result);
			
	        XPathNodeIterator ni=nav.Select("//Attribute");
	        while(ni.MoveNext())
	        {
	            string name=ni.Current.GetAttribute("name","");
	            string value=ni.Current.GetAttribute("value","");
	            if(name.Length>0)
	                result[name]=value;
	        }

	        ni=nav.Select("/Message/Item/Service");
	        if(ni.MoveNext())
	        {
	            result["resultcode"]=ni.Current.GetAttribute("resultcode","");
	            result["service_id"]=ni.Current.GetAttribute("id","");
	        }
			
	        ni=nav.Select("//Content/text()");
	        if(ni.MoveNext())
	        {
	            result["content"]=ni.Current.Value;
	        }



	        return result;
	    }


	    private static void addAttributesToHashtable(XmlAttributeCollection col, Hashtable table)
		{
			foreach(XmlAttribute attr in col)
			{
				table[attr.Name]=attr.Value;	
			}
			
		}

		private static void addAttributesToHashtable(XPathNodeIterator ni, Hashtable table)
		{
			while(ni.MoveNext())
			{
				table[ni.Current.LocalName]=ni.Current.Value;
			}
			
		}


		




		
		/// <summary>
		/// Attributes may be stored in "<Attribute>" element (name, value) or as an attribute of "<item>" element.
		/// </summary>
		/// <param name="xml"></param>
		/// <returns></returns>
		
		public static Hashtable getAttributesMap(string xml)
		{
			XmlDocument doc=new XmlDocument();	
			doc.LoadXml(xml);
			return getAttributesMap(doc);

		}


		public static XmlElement getSingleElement(XmlElement element,string name, bool canBeNull)
		{
			XmlNodeList elements=element.GetElementsByTagName(name);
			if(elements.Count>1)
				throw new Exception("Child element "+name+" of element "+element.Name+" must be there only once.");
			if(elements.Count==0)
				if(canBeNull)
					return null;
				else
					throw new Exception("Element "+name+" is missing");
			return (XmlElement)elements[0];

		}






#endregion

		/// <summary>
		/// Returns a subset of passed HashMap which keys begin with given prefix.
		/// Keys of returned hashtable are original keys without prefix.  
		/// </summary>
		/// <param name="map"></param>
		/// <param name="prefix"></param>
		/// <returns></returns>
		public static Hashtable getItemsWithPrefix(Hashtable map, string prefix) 
		{
			Hashtable result=new Hashtable();
			foreach (DictionaryEntry entry in map)
			{
				if(entry.Key.ToString().StartsWith(prefix))
					result.Add(entry.Key.ToString().Substring(prefix.Length),entry.Value);
			}
			return result;
		} 

		private static Regex variablePattern=new Regex(@"(\$)\{[^}]*\}");

		/// <summary>
		/// Trims first two characters from the begin and last one character from the end.
		/// </summary>
		/// <param name="s"></param>
		/// <returns></returns>
		public static string strip(string s)
		{
			if(s.Length<3)
				return String.Empty;

			return s.Substring(2,s.Length-3);
		}


		public static string fillTemplate(string template,Hashtable values)
		{
            if (template == null)
                template = String.Empty;

            if (values == null)
                values = new Hashtable();
            
            MatchCollection expressions = variablePattern.Matches(template);
			foreach(Match exp in expressions)
			{
					string value;
					string name = strip(exp.Value);
					if(!values.ContainsKey(name)) 
						name = name.ToLower();
					if(!values.ContainsKey(name))
						throw new ArgumentException(String.Format("Could not find value: {0}",exp.Value));
					value = XmlEntitiesEncode((string)values[name]);
					template = template.Replace(exp.ToString(),value);
			}
			return template;
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="table"></param>
		/// <param name="list"></param>
		public static void removeFrom(Hashtable table,ArrayList list)
		{
			Debug.Assert(table != null);
			Debug.Assert(list != null);
			foreach (string s in list)
			{
				table.Remove(s);
			}
		}
	    
	    public static void removeFrom<TKey, TValue>(IDictionary<TKey, TValue> table, IList<TKey> list)
	    {
            Debug.Assert(table != null);
            Debug.Assert(list != null);
	        foreach (TKey t in list)
	        {
                table.Remove(t);
	        }
	    }

		public static object getObjectFromXmlElement(Type type, XmlElement element, string name)
		{
				XmlNodeList elements = element.GetElementsByTagName(name);
				if(elements.Count>0)
				{
					XmlElement xe=(XmlElement) elements[0];
					XmlSerializer xs=new XmlSerializer(type);
					StringReader sr = new StringReader(xe.OuterXml);
					object obj = xs.Deserialize(sr);
					sr.Close();
					return obj;
				}
				return null;
		}

		public static void setFileContent(string fileName, string xml)
		{
			StreamWriter sw=new StreamWriter(getConfigPath(fileName));
			sw.Write(xml);
			sw.Close();
		}

		public static string GetIntendedXml(XmlNode e)
		{
            if (e == null)
                throw new ArgumentNullException("xmlNode");

            StringBuilder sb = new StringBuilder();
            using (XmlTextWriter writer = new XmlTextWriter(new StringWriter(sb)))
            {
                writer.Formatting = Formatting.Indented;
                writer.Indentation = 3;
                e.WriteTo(writer);
                writer.Close();
                return sb.ToString();
            }
		}

		public static string GetIntendedXml(string xml)
		{
            if (xml == null)
                throw new ArgumentNullException("xml");

            XmlDocument doc = new XmlDocument();
			try
			{
				doc.LoadXml(String.Format("<c>{0}</c>" , xml));
				return GetIntendedXml(doc.DocumentElement.FirstChild);
			}
			catch(Exception)
			{
				return String.Empty;
			}			
			
		}

	    public static string EvaluateXPathString(string expression, XPathNavigator nav)
	    {
            XPathNavigator navigator = nav;
            if (nav == null)
                navigator = fakeXPathNav;
            try
            {
                XPathExpression expr = navigator.Compile(expression);
                expr.SetContext(new XSLTDocumentContext());
                object result = navigator.Evaluate(expr);
                XPathNodeIterator pni = result as XPathNodeIterator;
                if (pni!=null)
                {
                    if (pni.MoveNext())
                        return pni.Current.ToString();
                    else
                        return String.Empty;
                }
                return result.ToString();
            }
            catch (Exception e)
            {
                string res;
                if (expression != null)
                    res = "null";
                else
                    res = expression;

                throw new InvalidExpressionException("Prepared expression is: " + res, e);
            }
	    }
	    
		private  static XPathDocument fakeXPathDoc = new XPathDocument(new StringReader("<root />"));
        private static XPathNavigator fakeXPathNav = fakeXPathDoc.CreateNavigator();
	    
		public static string EvaluateXPathString(string expression, IXPathNavigable obj )
		{
            IXPathNavigable useobj = obj;
		    if(obj==null)
				useobj=fakeXPathDoc;
            
		    XPathNavigator navigator = useobj.CreateNavigator();
            return EvaluateXPathString(expression, navigator);
		}

		/// <summary>
		/// Returns true if query result is not 0, 'false' or empty string
		/// </summary>
		/// <param name="condition"></param>
		/// <param name="obj"></param>
		/// <returns></returns>
		public static bool EvaluateXPathBool(string condition, IXPathNavigable obj)
		{
			string result=EvaluateXPathString(condition,obj).ToLower();
			return ToBool(result);
		}

		public static bool ToBool(string result)
		{
			if(result==null)
				return false;

            if (result.Length != 0 &&
                String.Compare(result, "false", StringComparison.InvariantCultureIgnoreCase) != 0 &&
                String.Compare(result, "0", StringComparison.InvariantCultureIgnoreCase) != 0
                )
                return true;
            else
                return false;
		}

        private static bool oldBool(string result)
        {
            if (result == null)
                return false;

            return result.ToLower() != "false" && result != "0" && result != "";
        }

        public static void testBool()
        {
            Stopwatch sw = new Stopwatch();
            string result = "";
            int MAX_I = 1000000;

            bool res = false;
            sw.Start();
            for(int i=0; i<MAX_I; ++i)
            {
                res = oldBool(result);
            }
            sw.Stop();
            Console.WriteLine(res);
            Console.WriteLine("OldBool: {0} ms", sw.ElapsedMilliseconds);
            sw.Reset();

            sw.Start();
            for (int i = 0; i < MAX_I; ++i)
            {
                res = ToBool(result);
            }
            sw.Stop();
            Console.WriteLine(res);
            Console.WriteLine("ToBool: {0} ms", sw.ElapsedMilliseconds);
        }


        public static XmlDocument parseXml(string xml)
		{
			XmlDocument doc=new XmlDocument();
			doc.LoadXml(xml);
			return doc;
		}

		/// <summary>
		/// Merges two XML document info one. Root element's names must match
		/// </summary>
		/// <param name="xml1"></param>
		/// <param name="xml2"></param>
		/// <returns></returns>
		public static string mergeXml(string xml1, string xml2)
		{
			DataSet ds=new DataSet();
			StringReader sr1 = new StringReader(xml1);
			ds.ReadXml(sr1);
			sr1.Close();

			DataSet ds2=new DataSet();
			StringReader sr2 = new StringReader(xml2);
			ds2.ReadXml(sr2);
			sr2.Close();
			ds.Merge(ds2);

			return ds.GetXml();

		}

		

		/// <summary>
		/// Returns XML presentation of given object
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public static string SerializeObject(object obj)
		{
				XmlSerializer xs=new XmlSerializer(obj.GetType());
				StringBuilder sb=new StringBuilder();
				XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
				ns.Add("", "");
				xs.Serialize(new XmlFragmentWriter(new StringWriter(sb),false),obj,ns);
				return sb.ToString();
		}

		/// <summary>
		/// returns XmlDocument's interpretation in given object
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public static XmlDocument serializeToDocument(object obj)
		{
			XmlDocument doc=new XmlDocument();
			MemoryStream ms=new MemoryStream();
			XmlSerializer serializer=new XmlSerializer(obj.GetType());
			serializer.Serialize(ms,obj);
			ms.Seek(0,SeekOrigin.Begin);
			doc.Load(ms);
			return doc;
		}



		/// <summary>
		/// Deserializes object from XML file
		/// </summary>
		/// <param name="fileName"></param>
		/// <param name="type"></param>
		/// <returns></returns>
		public static object DeserializeObject(string fileName, Type type)
		{
			string file=getFileContent(fileName);
			return DeserializeObject(new StringReader(file),type);
			
		}

		
		public static object DeserializeObject(TextReader reader, Type type)
		{
			XmlSerializer xs=new XmlSerializer(type);
			return xs.Deserialize(reader);
			
		}

		/// <summary>
		/// Parses string parameter representation into hashtable.
		/// </summary>
		/// <param name="parameters">Syntax is key1=value;key2=value;key3=.....</param>
		/// <returns>Returned hashtable is case-insensitive</returns>
		public static Hashtable ParseParameters(string parameters)
		{
			Hashtable ht=new HashtableCI();
			if (parameters == null)
				throw new ArgumentNullException("parameters");
			
			if (parameters.Length==0)
				return ht;
			string[] values=parameters.Split(';');
			foreach (string value in values)
			{
				string[] s=value.Split('=');
				ht.Add(s[0],s[1]);
			}
			return ht;
		}
		
		public static string UnparseParameters(Hashtable hash)
		{
			if (hash==null)
			{
			    throw new ArgumentNullException("hash");
			}
            
            StringBuilder sb = new StringBuilder();
			foreach (DictionaryEntry entry in hash)
			{
				sb.AppendFormat("{0}={1};", entry.Key, entry.Value);
			}
			sb.Remove(sb.Length - 1, 1);
			return sb.ToString();
		}


		public static void CopyDictionary(IDictionary source, IDictionary target, bool overwrite)
		{
            if (source == null)
                throw new ArgumentNullException("source");

            if (target == null)
                throw new ArgumentNullException("target");

            foreach (DictionaryEntry entry in source)
			{
				if(target.Contains(entry.Key) && !overwrite)
					continue;
				target[entry.Key]=entry.Value;
			}
		}

	
		
		/// <summary>
		/// Loads part of XML file into hashtable. "elementName" element will be used. 
		/// First attribute of this element will be used as key, second as value
		/// </summary>
		/// <returns></returns>
		public static Hashtable LoadIntoHashtable(string xml, string elementName)
		{
			XmlDocument doc=parseXml(xml)	;
			Hashtable result=new Hashtable();
			foreach (XmlElement el in doc.GetElementsByTagName(elementName))
			{
				result.Add(el.Attributes[0].Value,el.Attributes[1].Value);
			}
			return result;
		}

		public static string GetTextBoxText(string text)
		{
			string textBoxText = text.Replace("\x00", "[0]");
			return textBoxText;
		}

		public static string getString(int from, int to)
		{
			StringBuilder sb = new StringBuilder();
			sb.Append("start" + Environment.NewLine);
			for(int j = from; j<to; j++)
			{
				string hexa = ((char)j).ToString();
				sb.Append(j+ " - " + hexa + "AAA"+ Environment.NewLine);
			}
			sb.Append("end");
			return sb.ToString();
		}

		public static void CheckEmptyElement(string elementName, string xml)
		{
			XPathDocument xp=new XPathDocument(new StringReader(xml));
			if(xp.CreateNavigator().Select("//"+elementName+"[not(./*)]").MoveNext())
				throw new Exception("Your configuration "+xml+" contains empty element <"+elementName+" /> which is not allowed");

		}

		public static void CheckEmptyDeliveryTemplate(string xml)
		{
			XmlDocument doc=new XmlDocument();
			doc.LoadXml(xml);
			//only for IVR connector
			if(String.Compare(doc.DocumentElement.GetAttribute("connector"), "IVR") != 0)
				return;

			foreach(XmlElement el in doc.GetElementsByTagName("deliveryTemplate"))
			{
				if(el.GetAttribute("direction").Length==0 && el.GetAttribute("connector").Length==0)
				{
					throw new Exception("deliveryTemplate for IVR must contain at least direction or connector attribute");
				}

				if(el.GetElementsByTagName("set").Count>0)
				{
					throw new Exception("deliveryTemplate for IVR must not contain any <Set> element.");
				}

			}
		}

        public static int NumberOfProcessors()
        {
            return Environment.ProcessorCount;
        }

        public static bool couldBeAscii(string str)
        {
            byte[] strByte = Encoding.ASCII.GetBytes(str);
            byte[] unicodeBytes = new byte[strByte.Length * 2];

            for (int i = 0; i < strByte.Length; ++i)
            {
                unicodeBytes[2 * i] = strByte[i];
                unicodeBytes[2 * i + 1] = 0;
            }

            string res = Encoding.Unicode.GetString(unicodeBytes);
            return String.CompareOrdinal(str, res) == 0;
        }


		#region Common regular expressions

		public const string EmailRegExp = @"^([\w-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([\w-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$";
		public const string DNSRegExp  = "^[-_a-zA-Z0-9]+(\\.[-_a-zA-Z0-9])*$";
		public const string PositiveIntRegExp = "^[0-9]+$";
		public const string AccountPasswordRegExp = "^[\\.-_\\w]*$";

		#endregion

		private FUtil()
		{
		}

	
	}
}

	

